package com.bauer.framework.autumn.factory.iml;

import com.bauer.framework.autumn.annotation.Bean;
import com.bauer.framework.autumn.annotation.Injected;
import com.bauer.framework.autumn.annotation.paser.AnnotationPaser;
import com.bauer.framework.autumn.beandefination.BeanDefination;
import com.bauer.framework.autumn.factory.BeanFactory;
import com.bauer.framework.autumn.utils.AnnotationUtils;


import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author ：rui.wei
 * @Date ：Created in 14:22 2018/7/20
 * @Description：
 */
public class AutowiredBeanFactory implements BeanFactory {

    private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();

    private ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>();

    private AnnotationPaser annotationPaser;

    private List<String> beanNames = new ArrayList<>();

    @Override
    public Object getBean(String name) {
        if (name == null) {
            return null;
        }

        if (beanMap.get(name) != null) {
            return beanMap.get(name);
        }

        BeanDefination beanDefination = beanDefinationMap.get(name);
        Object bean = null;
        if (beanDefination != null) {

            try {
                bean = injectBean(beanDefination.getBeanClass());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bean;
        }

        return null;
    }

    @Override
    public void loadBeanDefination(ClassLoader classLoader, List<String> path) {
        try {
            for (String pkg : path) {
                Class loadClass = classLoader.loadClass(pkg);
                annotationPaser =  new AnnotationPaser(loadClass);
                if (annotationPaser.hasAnnotion(Bean.class.getName())) {
                    BeanDefination beanDefination = new BeanDefination();
                    beanDefination.setName(loadClass.getName());
                    beanDefination.setBeanClass(loadClass);
                    beanNames.add(loadClass.getName());
                    beanDefinationMap.put(beanDefination.getName(), beanDefination);
                }

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private Object injectBean(Class beanClass) throws Exception {
        //如果beandefination中没有返回null
        if (beanDefinationMap.get(beanClass.getName()) == null) {
            return null;
        }

        Object bean = beanClass.newInstance();
        beanMap.put(beanClass.getName(), bean);
        Field[] fields = beanClass.getDeclaredFields();
        for (Field field : fields) {
            Annotation[] annotations = field.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof Injected) {
                    String fieldBeanName = field.getType().getName();
                    Injected injected = (Injected) annotation;

                    //缓存中存在bean 对象
                    Object cachedBean = beanMap.get(fieldBeanName);
                    field.setAccessible(true);
                    if (cachedBean != null) {
                        field.set(bean, cachedBean);
                    } else {
                        Object newBean = injectBean(field.getType());

                        //判断是否必须注入
                        if (injected.requierd() && newBean == null) {
                            throw new RuntimeException();
                        }
                        field.set(bean, newBean);
                    }
                }
            }
        }
        return bean;
    }


    @Override
    public void initAllBean() {
        Iterator map1it = beanDefinationMap.entrySet().iterator();
        while (map1it.hasNext()) {
            Map.Entry<String, BeanDefination> entry = (Entry<String, BeanDefination>) map1it.next();
            try {
                injectBean(entry.getValue().getBeanClass());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public List<String> getBeanNames() {
        return beanNames;
    }

    @Override
    public BeanDefination getBeanDefination(String beanName) {
        return beanDefinationMap.get(beanName);
    }

    @Override
    public <A extends Annotation> A findAnnotationOnBean(Class beanClasss,
            Class<A> annotationType) {
        return AnnotationUtils.findAnnotationOnBean(beanClasss,annotationType);
    }
}
